/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.binary;

import com.cv4j.core.datamodel.ByteProcessor;
import com.cv4j.exception.CV4JException;

import java.util.Arrays;

/**
 * 链码
 *
 * @author Administrator
 * @date 2021/07/27
 */
public class ChainCode extends CourtEdge {
    /**
     * 过程
     *
     * @param binary 二进制
     * @param codeMap 代码映射
     */
    public void process(ByteProcessor binary, int[] codeMap) {
        super.process(binary);
        int width = binary.getWidth();
        int height = binary.getHeight();
        if (codeMap.length != (width * height)) {
            throw new CV4JException("chain code map length assert failure");
        }

        byte[] input = binary.getGray();
        byte[] output = new byte[width * height];
        System.arraycopy(input, 0, output, 0, input.length);

        // initialization code map
        Arrays.fill(codeMap, -1);
        int offset = 0;
        for (int row = 0; row < height; row++) {
            offset = row * width;
            int[] size = new int[]{width, height};
            changeCodeMap(codeMap, size, input, offset, row);
        }
    }

    private void changeCodeMap(int[] codeMap, int[] size, byte[] input, int offset, int row) {
        int width = size[0];
        for (int col = 0; col < width; col++) {
            int pv = input[offset + col] & 0xff;
            if (pv == 255) {
                // do something here!!!
                int code = getRelationship(input, codeMap, row, col, size);
                if (code >= 0) {
                    codeMap[offset + col] = code;
                }
            }
        }
    }

    /**
     * 得到的关系
     *
     * @param pixels1 pixels1
     * @param codemap codemap
     * @param row 行
     * @param col 上校
     * @param size 0宽度 1高度
     * @return int
     */
    private int getRelationship(byte[] pixels1, int[] codemap, int row, int col, int[] size) {
        int width = size[0];
        int height = size[1];
        int offset = row * width;
        if ((col + 1) < width) {
            if (rel0(col, pixels1, codemap, offset)) {
                return 0;
            }
        }
        if ((col + 1) < width && (row + 1) < height) {
            if (rel1(col, pixels1, codemap, offset, width)) {
                return 1;
            }
        }
        if ((row + 1) < height) {
            if (rel2(col, pixels1, codemap, offset, width)) {
                return 2;
            }
        }
        if ((col - 1) >= 0 && (row + 1) < height) {
            if (rel3(col, pixels1, codemap, offset, width)) {
                return 3;
            }
        }
        if ((col - 1) >= 0) {
            if (rel4(col, pixels1, codemap, offset)) {
                return 4;
            }
        }
        if ((col - 1) >= 0 && (row - 1) >= 0) {
            if (rel5(col, pixels1, codemap, offset, width)) {
                return 5;
            }
        }
        if ((row - 1) >= 0) {
            if (rel6(col, pixels1, codemap, offset, width)) {
                return 6;
            }
        }
        if ((row - 1) >= 0 && (col + 1) < width) {
            if (rel7(col, pixels1, codemap, offset, width)) {
                return 7;
            }
        }
        return -2; // invalid, stop condition
    }

    private boolean rel0(int col, byte[] pixels1, int[] codemap, int offset) {
        int pv = pixels1[offset + col + 1] & 0xff;
        int c1 = codemap[offset + col + 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel1(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset + width + col + 1] & 0xff;
        int c1 = codemap[offset + width + col + 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel2(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset + width + col] & 0xff;
        int c1 = codemap[offset + width + col];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel3(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset + width + col - 1] & 0xff;
        int c1 = codemap[offset + width + col - 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel4(int col, byte[] pixels1, int[] codemap, int offset) {
        int pv = pixels1[offset + col - 1] & 0xff;
        int c1 = codemap[offset + col - 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel5(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset - width + col - 1] & 0xff;
        int c1 = codemap[offset - width + col - 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel6(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset - width + col] & 0xff;
        int c1 = codemap[offset - width + col];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }

    private boolean rel7(int col, byte[] pixels1, int[] codemap, int offset, int width) {
        int pv = pixels1[offset - width + col + 1] & 0xff;
        int c1 = codemap[offset - width + col + 1];
        if (pv == 255 && c1 < 0) {
            return true;
        }
        return false;
    }
}
